#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif // use linux

#include <cstdio>
#include <cerrno>
#include <cstdlib>
#include <cassert>
#include <cstdint>
#include <ctime>
#include <cstring>

#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
#include <ftw.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

#include <string>
#include <list>
#include <map>
#include <set>
#include <vector>

#if defined(__APPLE__) // FIXME: I can not test on apple device, cause I don't have macOs
#include <sys/uio.h>
#elif defined(__linux__)
#include <sys/sendfile.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#endif // unix platform headers 

#include "event.h" // TODO: where is this file, libev ?
#include "gp_error.h"

/// @desc: this header file 
#ifndef GKO_POOL_INCLUDE_GINGKO_H
#define GKO_POOL_INCLUDE_GINGKO_H

#define USE_GKO_MEMORY_POOL
#define NO_SO_CALLED_FNV_OPTIMIZE
#define ROT_XOR_HASH
#define _FILE_OFFSET_BITS 64
#define HASH_BYTE_NUM_ONCE 8
#define CMD_PREFIX_BYTE 10

#if CMD_PREFIX_BYTE == 10
#define PREFIX_CMD "0000000000" // 10 of 0
#endif // cmd prefix bytes thing

#define CMD_LEN 8

// TODO: rename as typedef
using GKO_UINT64 = unsigned long long;
using GKO_INT64 = long long;
using GKO_CONST_STR = const char* const;

#if defined(_GKO_VERSION)
static GKO_CONST_STR _GKO_VERSION = _GKO_VERSION; // why define version here
#else
static GKO_CONST_STR GKO_VERSION  = "Unknown(who fucking cares)";
#endif // gko version thing

static const short PROTO_VER = '0'; // protocol version
static const uid_t ROOT = 0; // root user uid
static const int BLOCK_SIZE = (2 * 1024 * 1024); // TODO: add below docs
static const int UNZIP_EXTRA = 32; // 8 bytes is enough
static const int MAX_REQ_SERV_BLOCKS = 5;
static const int MAX_REQ_CLNT_BLOCKS = 300;
static const int SERV_PORT = 2120;
// TODO: a lot of static value define

/// @sepline: line for seperate static value above and defines below
#define OFFSETOF(s, i) ((unsigned) & ((s *)0)->i)

#ifndef MIN
#define MIN(a. b) ((a) < (b) ? (a) : (b)) // a < b return a
#endif 

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b)) // a > b return a
#endif
/// @desc: a % block_size not 0 return a / block_size + 1 else return a / block_size
#define BLOCK_COUNT(a) \
    ( ( (a) % (BLOCK_SIZE) ) ? ( (a) / (BLOCK_SIZE) + 1 ) : ( (a) / (BLOCK_SIZE) ) )

// TODO:
/// @sepline: below is typedefs and structs
#pragma pack (4)

typedef struct _s_job_t s_job_t;
typedef void* (*func_t)(void* _1, int _2);
typedef void* (*ProcessHandler_t)(void* _1);
typedef void (*ReportHandler_t)(void* _1, const char* _2);
typedef int (*HMTRHandler_t)(void* _1, const char* _2, const int _3);

typedef struct _s_file_t
{
    struct stat f_stat;
    mode_t mode;
    GKO_INT64 size; // -1 for dir, -2 for symbol link
    char sympath[MAX_PATH_LEN];
    char name[MAX_PATH_LEN];
    u_int8_t md5[16];
} s_file_t;

typedef struct _s_host_t
{
    char addr[HOSTNAME_LEN];
    int port;
} s_host_t;

typedef struct _s_block_t
{
    GKO_INT64 size;
    GKO_INT64 start_off;
    GKO_INT64 start_f;
    u_int8_t done;

    unsigned int digest;
    std::set<s_host_t>* host_set; // only used by client, lock here
} s_block_t;

typedef struct
{
    GKO_INT64 range[2];
    s_job_t* p;
    u_int8_t* buf;
    int index;
} hash_worker_thread_arg;

typedef struct _s_job_t
{
    unsigned int job_state; // pass job state to client
    int host_num;
    int host_set_max_size; // keep track of the max host_set size

    int lock_id;
    struct timeval dl_time; // time used for download

    char uri[MAX_URI_LEN]; // TODO: rename MAX_URI -> MAX_URI_LEN
    char path[MAX_PATH_LEN]; // local path, server side and client are diff
    
    s_file_t* files;
    GKO_INT64 files_size;
    s_block_t* blocks;
    GKO_INT64 blocks_size;

    std::set<s_host_t>* host_set;
    GKO_INT64 file_count;
    GKO_INT64 block_count;
    GKO_INT64 total_size;
    GKO_INT64 hash_progress[XOR_HASH_TNUM]; // the hash progress in byte

    pthread_t hash_worker[XOR_HASH_TNUM];
    u_int8_t* hash_buf[XOR_HASH_TNUM];
    hash_worker_thread_arg arg[XOR_HASH_TNUM];
} s_job_t;

#pragma pack () // end of pragma section

/// @sepline: funcs below


#endif // header file protect